/*******************************************************************************
 * Copyright 2011 Mauro Luigi Drago (drago.luigi@gmail.com)
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *******************************************************************************/
package com.bytenose.extrautils.concurrent;

import com.bytenose.extrautils.annotations.ThreadSafe;

/**
 * <p>A cancellation token to signal and propagate cancellation
 * requests when executing asynchronous operations.</p>
 * 
 * <p>All the methods of this class are thread safe and may be used concurrently</p>
 * 
 * <p>The interface of this class is inspired (and copied for some of its parts) 
 * from the Microsoft .NET 4 platform.</p>
 * 
 * @author Mauro Luigi Drago
 *
 */
@ThreadSafe
public final class CancellationToken {
	/** 
	 * The states regulating the behavior of this class.
	 * A newly created token starts in the NOT_FIXED state.
	 * Whenever the isCancellationRequested attribute is set
	 * (no matter the value), the state changes to FIXED.
	 * In the FIXED state, no more changes to the cancellation
	 * token are possible.
	 */
	private enum TokenState { FIXED, NOT_FIXED };
	
	/** The current state of the token */
	private TokenState tokenState;
	
	/** A flag indicating whether cancellation has been requested */
	private boolean isCancellationRequested;
	
	/**
	 * Creates a new {@link CancellationToken} with the 
	 * canceled state set to false and which
	 * may be subsequently canceled.
	 */
	CancellationToken() {
		tokenState = TokenState.NOT_FIXED;
		isCancellationRequested = false;
	}
	
	/**
	 * <p>Creates a new {@link CancellationToken} with a fixed
	 * and immutable cancellation state.</p>
	 * 
	 * <p>This constructor is useful when asynchronous operations
	 * cannot be canceled.</p>
	 * @param canceled
	 * 		the canceled state for this token.
	 */
	public CancellationToken(boolean canceled) {
		tokenState = TokenState.FIXED;
		isCancellationRequested = canceled;
	}
	
	/**
	 * Checks whether cancellation has been requested for this token.
	 * @return true if cancellation has been requested, false otherwise.
	 */
	public synchronized boolean isCancellationRequested() {
		return isCancellationRequested;
	}
	
	/**
	 * Changes the cancellation state of this token to canceled.
	 * This method has no effect if the token is already in the canceled state.
	 * 
	 * @throws IllegalStateException
	 * 		if the token is already in the FIXED state.
	 */
	synchronized void cancel() throws IllegalStateException {
		if (isCancellationRequested) return;
		if (tokenState == TokenState.FIXED)
			throw new IllegalStateException("Cannot change the state of a fixed cancellation token");
		
		isCancellationRequested = true;
		tokenState = TokenState.FIXED;
	}
}
